using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;

using Patterns.Tools.Properties;

namespace Patterns.Tools.CodeGenerators.GoogleCode
{
	public static class WikiHelper
	{
		private static readonly IDictionary<string, Func<XElement, string>> _summaryElementRenderers
			= new Dictionary<string, Func<XElement, string>>
			{
				{ "see", xml => ResolveReference(xml.Attribute("cref").Value) },
				{ "paramref", xml => string.Format("*{0}*", xml.Attribute("name").Value) }
			};

		private static readonly IDictionary<EnumRange, Func<XNode, string>> _summaryRenderers
			= new Dictionary<EnumRange, Func<XNode, string>>
			{
				{
					EnumRange.From(XmlNodeType.Element), node =>
					{
						var xml = (XElement) node;
						string name = xml.Name.LocalName;
						return _summaryElementRenderers.ContainsKey(name) 
							? _summaryElementRenderers[name](xml) 
							: string.Format("_`***unknown documentation element: {0}***`_", name);
					}
				},
				{ EnumRange.From(XmlNodeType.Text, XmlNodeType.CDATA), node => ((XText) node).Value },
				{ EnumRange.From(XmlNodeType.SignificantWhitespace), node => node.ToString(SaveOptions.DisableFormatting) }
			};

		public static string ConvertToFolderUri(string name)
		{
			var nameBreaks = new Regex(@"[\.]+");
			return nameBreaks.Replace(name, "/").Trim('/');
		}

		public static string ConvertToFileUri(string name, string extension)
		{
			return string.Format("{0}.{1}", ConvertToFolderUri(name), extension);
		}

		public static int GetMinimumUnderscoreCount(IEnumerable<string> items)
		{
			IEnumerable<int> counts = items.Select(GetUnderscoreCount).ToArray();
			return counts.Any() ? counts.Min() : 0;
		}

		public static int GetUnderscoreCount(string item)
		{
			var pattern = new Regex("_");
			var genericPattern = new Regex(@"_\d+$");
			return pattern.Matches(genericPattern.Replace(item, string.Empty)).Count;
		}

		public static string GetAssemblyVersion(Assembly assembly)
		{
			var attribute = assembly.GetCustomAttributes(typeof (AssemblyVersionAttribute), true).OfType<AssemblyVersionAttribute>().FirstOrDefault();
			var fileAttribute = assembly.GetCustomAttributes(typeof (AssemblyFileVersionAttribute), true).OfType<AssemblyFileVersionAttribute>().FirstOrDefault();
			return attribute == null ? fileAttribute == null ? string.Empty : fileAttribute.Version : attribute.Version;
		}

		public static string GetAssemblyDescription(Assembly assembly)
		{
			var attribute = assembly.GetCustomAttributes(typeof (AssemblyDescriptionAttribute), true).OfType<AssemblyDescriptionAttribute>().FirstOrDefault();

			if (attribute == null || string.IsNullOrEmpty(attribute.Description))
			{
				Console.WriteLine(Resources.MissingAssemblyDescription, assembly.GetName().Name);
				return string.Empty;
			}

			return attribute.Description;
		}

		public static string GetTypeSummary(Type type, XElement xml)
		{
			return GetSummary(xml, string.Format("T:{0}", type.FullName));
		}

		public static string GetEnumOptionSummary(Type type, string option, XElement xml)
		{
			return GetSummary(xml, string.Format("F:{0}.{1}", type.FullName, option));
		}

		public static string Escape(string text)
		{
			return text.Replace("*", "`*`");
		}

		public static string GetParentWikiName(bool hasParentNamespace, string parent)
		{
			return hasParentNamespace ? WikiNames.Namespace(parent) : WikiNames.Assembly(parent);
		}

		public static string GetParentName(string name)
		{
			var pattern = new Regex(@"(?<parent>.+)\.[^\.]+$");
			return pattern.Match(name).Groups["parent"].Value;
		}

		private static string GetSummary(XContainer xml, string memberName)
		{
			XElement summary = xml.Descendants("member")
				.Where(item => StringComparer.OrdinalIgnoreCase.Equals(item.Attribute("name").Value, memberName))
				.Select(item => item.Element("summary"))
				.FirstOrDefault();

			if (summary == null) return string.Empty;

			return string.Concat(summary.Nodes().Select(node =>
			{
				EnumRange key = EnumRange.From(node.NodeType);
				return _summaryRenderers.ContainsKey(key) ? _summaryRenderers[key](node) : string.Empty;
			}));
		}

		private static string ResolveReference(string value)
		{
			return string.Format("_`*** reference resolution coming soon. reference: {0} ***`_", value);
		}
	}
}